Big TreeView DnD fixage, makes drops on empty models work, makes TreeStore
authorKristian Rietveld <kris@gtk.org>
Tue, 9 Sep 2003 23:13:39 +0000 (23:13 +0000)
committerKristian Rietveld <kristian@src.gnome.org>
Tue, 9 Sep 2003 23:13:39 +0000 (23:13 +0000)
Wed Sep 10 01:06:44 2003  Kristian Rietveld  <kris@gtk.org>

Big TreeView DnD fixage, makes drops on empty models work, makes
TreeStore DnD work and gets rid of gtk-tree-model-drop-append.
Related bugs #95362 and #113314. I don't want to touch this code
ever again.

* gtk/gtktreeprivate.h (GtkTreePrivate): add empty_view_drop field.

* gtk/gtktreednd.c (gtk_tree_get_row_drag_data): add check
for selection_data->length.

* gtk/gtktreeview.c (struct DestRow), (dest_row_free),
(set_dest_row), (get_dest_row): we don't store just the row ref
anymore, but a struct with the row ref and additional info,
(set_destination_row): handle drops on empty space and some
style fixes,
(get_logical_dest_row): also return path_down_mode/drop_append_mode
flags, handle dropping childs on their new parents, rewrite
drop append handling into something saner,
(gtk_tree_view_drag_motion): show a "drop possible arrow" on empty
spaces,
(gtk_tree_view_drag_drop): updates for updated backend,
(gtk_tree_view_drag_data_received): updates for updated backend,
path down mode (treestore DnD) handling,
(gtk_tree_view_set_drag_dest_row): set empty_view_drop flag,
when we are trying to drop a row on an empty model,
(gtk_tree_view_get_drag_dest_row): handle empty_view_drop flag.

* gtk/gtkliststore.c (gtk_list_store_drag_data_received),
(gtk_list_store_row_drop_possible): style and drop-append fixes.

* gtk/gtktreestore.c (gtk_tree_store_drag_data_received): ditto.

ChangeLog
ChangeLog.pre-2-10
ChangeLog.pre-2-4
ChangeLog.pre-2-6
ChangeLog.pre-2-8
gtk/gtkliststore.c
gtk/gtktreednd.c
gtk/gtktreeprivate.h
gtk/gtktreestore.c
gtk/gtktreeview.c

index 5aab8e8c6ff680d1caecbf3f48c0f682f77eac01..76d5f998fc7278333c9bd103c62b16e378f4d7f1 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,37 @@
+Wed Sep 10 01:06:44 2003  Kristian Rietveld  <kris@gtk.org>
+
+       Big TreeView DnD fixage, makes drops on empty models work, makes
+       TreeStore DnD work and gets rid of gtk-tree-model-drop-append.
+       Related bugs #95362 and #113314. I don't want to touch this code
+       ever again.
+
+       * gtk/gtktreeprivate.h (GtkTreePrivate): add empty_view_drop field.
+
+       * gtk/gtktreednd.c (gtk_tree_get_row_drag_data): add check
+       for selection_data->length.
+
+       * gtk/gtktreeview.c (struct DestRow), (dest_row_free),
+       (set_dest_row), (get_dest_row): we don't store just the row ref
+       anymore, but a struct with the row ref and additional info,
+       (set_destination_row): handle drops on empty space and some
+       style fixes,
+       (get_logical_dest_row): also return path_down_mode/drop_append_mode
+       flags, handle dropping childs on their new parents, rewrite
+       drop append handling into something saner,
+       (gtk_tree_view_drag_motion): show a "drop possible arrow" on empty
+       spaces,
+       (gtk_tree_view_drag_drop): updates for updated backend,
+       (gtk_tree_view_drag_data_received): updates for updated backend,
+       path down mode (treestore DnD) handling,
+       (gtk_tree_view_set_drag_dest_row): set empty_view_drop flag,
+       when we are trying to drop a row on an empty model,
+       (gtk_tree_view_get_drag_dest_row): handle empty_view_drop flag.
+
+       * gtk/gtkliststore.c (gtk_list_store_drag_data_received),
+       (gtk_list_store_row_drop_possible): style and drop-append fixes.
+
+       * gtk/gtktreestore.c (gtk_tree_store_drag_data_received): ditto.
+
 2003-09-08  Alexander Larsson  <alexl@redhat.com>
 
        * gtk/gtkeventbox.[hc]:
index 5aab8e8c6ff680d1caecbf3f48c0f682f77eac01..76d5f998fc7278333c9bd103c62b16e378f4d7f1 100644 (file)
@@ -1,3 +1,37 @@
+Wed Sep 10 01:06:44 2003  Kristian Rietveld  <kris@gtk.org>
+
+       Big TreeView DnD fixage, makes drops on empty models work, makes
+       TreeStore DnD work and gets rid of gtk-tree-model-drop-append.
+       Related bugs #95362 and #113314. I don't want to touch this code
+       ever again.
+
+       * gtk/gtktreeprivate.h (GtkTreePrivate): add empty_view_drop field.
+
+       * gtk/gtktreednd.c (gtk_tree_get_row_drag_data): add check
+       for selection_data->length.
+
+       * gtk/gtktreeview.c (struct DestRow), (dest_row_free),
+       (set_dest_row), (get_dest_row): we don't store just the row ref
+       anymore, but a struct with the row ref and additional info,
+       (set_destination_row): handle drops on empty space and some
+       style fixes,
+       (get_logical_dest_row): also return path_down_mode/drop_append_mode
+       flags, handle dropping childs on their new parents, rewrite
+       drop append handling into something saner,
+       (gtk_tree_view_drag_motion): show a "drop possible arrow" on empty
+       spaces,
+       (gtk_tree_view_drag_drop): updates for updated backend,
+       (gtk_tree_view_drag_data_received): updates for updated backend,
+       path down mode (treestore DnD) handling,
+       (gtk_tree_view_set_drag_dest_row): set empty_view_drop flag,
+       when we are trying to drop a row on an empty model,
+       (gtk_tree_view_get_drag_dest_row): handle empty_view_drop flag.
+
+       * gtk/gtkliststore.c (gtk_list_store_drag_data_received),
+       (gtk_list_store_row_drop_possible): style and drop-append fixes.
+
+       * gtk/gtktreestore.c (gtk_tree_store_drag_data_received): ditto.
+
 2003-09-08  Alexander Larsson  <alexl@redhat.com>
 
        * gtk/gtkeventbox.[hc]:
index 5aab8e8c6ff680d1caecbf3f48c0f682f77eac01..76d5f998fc7278333c9bd103c62b16e378f4d7f1 100644 (file)
@@ -1,3 +1,37 @@
+Wed Sep 10 01:06:44 2003  Kristian Rietveld  <kris@gtk.org>
+
+       Big TreeView DnD fixage, makes drops on empty models work, makes
+       TreeStore DnD work and gets rid of gtk-tree-model-drop-append.
+       Related bugs #95362 and #113314. I don't want to touch this code
+       ever again.
+
+       * gtk/gtktreeprivate.h (GtkTreePrivate): add empty_view_drop field.
+
+       * gtk/gtktreednd.c (gtk_tree_get_row_drag_data): add check
+       for selection_data->length.
+
+       * gtk/gtktreeview.c (struct DestRow), (dest_row_free),
+       (set_dest_row), (get_dest_row): we don't store just the row ref
+       anymore, but a struct with the row ref and additional info,
+       (set_destination_row): handle drops on empty space and some
+       style fixes,
+       (get_logical_dest_row): also return path_down_mode/drop_append_mode
+       flags, handle dropping childs on their new parents, rewrite
+       drop append handling into something saner,
+       (gtk_tree_view_drag_motion): show a "drop possible arrow" on empty
+       spaces,
+       (gtk_tree_view_drag_drop): updates for updated backend,
+       (gtk_tree_view_drag_data_received): updates for updated backend,
+       path down mode (treestore DnD) handling,
+       (gtk_tree_view_set_drag_dest_row): set empty_view_drop flag,
+       when we are trying to drop a row on an empty model,
+       (gtk_tree_view_get_drag_dest_row): handle empty_view_drop flag.
+
+       * gtk/gtkliststore.c (gtk_list_store_drag_data_received),
+       (gtk_list_store_row_drop_possible): style and drop-append fixes.
+
+       * gtk/gtktreestore.c (gtk_tree_store_drag_data_received): ditto.
+
 2003-09-08  Alexander Larsson  <alexl@redhat.com>
 
        * gtk/gtkeventbox.[hc]:
index 5aab8e8c6ff680d1caecbf3f48c0f682f77eac01..76d5f998fc7278333c9bd103c62b16e378f4d7f1 100644 (file)
@@ -1,3 +1,37 @@
+Wed Sep 10 01:06:44 2003  Kristian Rietveld  <kris@gtk.org>
+
+       Big TreeView DnD fixage, makes drops on empty models work, makes
+       TreeStore DnD work and gets rid of gtk-tree-model-drop-append.
+       Related bugs #95362 and #113314. I don't want to touch this code
+       ever again.
+
+       * gtk/gtktreeprivate.h (GtkTreePrivate): add empty_view_drop field.
+
+       * gtk/gtktreednd.c (gtk_tree_get_row_drag_data): add check
+       for selection_data->length.
+
+       * gtk/gtktreeview.c (struct DestRow), (dest_row_free),
+       (set_dest_row), (get_dest_row): we don't store just the row ref
+       anymore, but a struct with the row ref and additional info,
+       (set_destination_row): handle drops on empty space and some
+       style fixes,
+       (get_logical_dest_row): also return path_down_mode/drop_append_mode
+       flags, handle dropping childs on their new parents, rewrite
+       drop append handling into something saner,
+       (gtk_tree_view_drag_motion): show a "drop possible arrow" on empty
+       spaces,
+       (gtk_tree_view_drag_drop): updates for updated backend,
+       (gtk_tree_view_drag_data_received): updates for updated backend,
+       path down mode (treestore DnD) handling,
+       (gtk_tree_view_set_drag_dest_row): set empty_view_drop flag,
+       when we are trying to drop a row on an empty model,
+       (gtk_tree_view_get_drag_dest_row): handle empty_view_drop flag.
+
+       * gtk/gtkliststore.c (gtk_list_store_drag_data_received),
+       (gtk_list_store_row_drop_possible): style and drop-append fixes.
+
+       * gtk/gtktreestore.c (gtk_tree_store_drag_data_received): ditto.
+
 2003-09-08  Alexander Larsson  <alexl@redhat.com>
 
        * gtk/gtkeventbox.[hc]:
index 5aab8e8c6ff680d1caecbf3f48c0f682f77eac01..76d5f998fc7278333c9bd103c62b16e378f4d7f1 100644 (file)
@@ -1,3 +1,37 @@
+Wed Sep 10 01:06:44 2003  Kristian Rietveld  <kris@gtk.org>
+
+       Big TreeView DnD fixage, makes drops on empty models work, makes
+       TreeStore DnD work and gets rid of gtk-tree-model-drop-append.
+       Related bugs #95362 and #113314. I don't want to touch this code
+       ever again.
+
+       * gtk/gtktreeprivate.h (GtkTreePrivate): add empty_view_drop field.
+
+       * gtk/gtktreednd.c (gtk_tree_get_row_drag_data): add check
+       for selection_data->length.
+
+       * gtk/gtktreeview.c (struct DestRow), (dest_row_free),
+       (set_dest_row), (get_dest_row): we don't store just the row ref
+       anymore, but a struct with the row ref and additional info,
+       (set_destination_row): handle drops on empty space and some
+       style fixes,
+       (get_logical_dest_row): also return path_down_mode/drop_append_mode
+       flags, handle dropping childs on their new parents, rewrite
+       drop append handling into something saner,
+       (gtk_tree_view_drag_motion): show a "drop possible arrow" on empty
+       spaces,
+       (gtk_tree_view_drag_drop): updates for updated backend,
+       (gtk_tree_view_drag_data_received): updates for updated backend,
+       path down mode (treestore DnD) handling,
+       (gtk_tree_view_set_drag_dest_row): set empty_view_drop flag,
+       when we are trying to drop a row on an empty model,
+       (gtk_tree_view_get_drag_dest_row): handle empty_view_drop flag.
+
+       * gtk/gtkliststore.c (gtk_list_store_drag_data_received),
+       (gtk_list_store_row_drop_possible): style and drop-append fixes.
+
+       * gtk/gtktreestore.c (gtk_tree_store_drag_data_received): ditto.
+
 2003-09-08  Alexander Larsson  <alexl@redhat.com>
 
        * gtk/gtkeventbox.[hc]:
index be14f2b28ca5eb6e16bdfc7dcf83b6c584373b81..5fb7d99dbadded096fff8a714ae1d37fd28a5cd4 100644 (file)
@@ -1497,32 +1497,22 @@ gtk_list_store_drag_data_received (GtkTreeDragDest   *drag_dest,
           /* dest was the first spot in the list; which means we are supposed
            * to prepend.
            */
-          gtk_list_store_prepend (GTK_LIST_STORE (tree_model),
-                                  &dest_iter);
+          gtk_list_store_prepend (list_store, &dest_iter);
 
           retval = TRUE;
         }
       else
         {
-          if (gtk_tree_model_get_iter (GTK_TREE_MODEL (tree_model),
-                                       &dest_iter,
-                                       prev))
+          if (gtk_tree_model_get_iter (tree_model, &dest_iter, prev))
             {
               GtkTreeIter tmp_iter = dest_iter;
 
-             if (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_model), "gtk-tree-model-drop-append")))
-               gtk_list_store_append (GTK_LIST_STORE (tree_model), &dest_iter);
-             else
-               gtk_list_store_insert_after (GTK_LIST_STORE (tree_model),
-                                            &dest_iter, &tmp_iter);
+              gtk_list_store_insert_after (list_store, &dest_iter, &tmp_iter);
 
               retval = TRUE;
             }
         }
 
-      g_object_set_data (G_OBJECT (tree_model), "gtk-tree-model-drop-append",
-                        NULL);
-
       gtk_tree_path_free (prev);
 
       /* If we succeeded in creating dest_iter, copy data from src
@@ -1554,11 +1544,11 @@ gtk_list_store_drag_data_received (GtkTreeDragDest   *drag_dest,
               ++col;
             }
 
-         dest_iter.stamp = GTK_LIST_STORE (tree_model)->stamp;
+         dest_iter.stamp = list_store->stamp;
           G_SLIST (dest_iter.user_data)->data = copy_head;
 
-         path = gtk_list_store_get_path (GTK_TREE_MODEL (tree_model), &dest_iter);
-         gtk_tree_model_row_changed (GTK_TREE_MODEL (tree_model), path, &dest_iter);
+         path = gtk_list_store_get_path (tree_model, &dest_iter);
+         gtk_tree_model_row_changed (tree_model, path, &dest_iter);
          gtk_tree_path_free (path);
        }
     }
@@ -1612,7 +1602,8 @@ gtk_list_store_row_drop_possible (GtkTreeDragDest  *drag_dest,
     retval = TRUE;
 
  out:
-  gtk_tree_path_free (src_path);
+  if (src_path)
+    gtk_tree_path_free (src_path);
   
   return retval;
 }
index 7d9c13677ec96858bd5686b8548fa21b37369e1a..0af3d0026741ed013c71cfea3784b465ae3c7f4b 100644 (file)
@@ -313,6 +313,9 @@ gtk_tree_get_row_drag_data (GtkSelectionData  *selection_data,
   if (selection_data->target != gdk_atom_intern ("GTK_TREE_MODEL_ROW", FALSE))
     return FALSE;
 
+  if (selection_data->length < 0)
+    return FALSE;
+
   trd = (void*) selection_data->data;
 
   if (tree_model)
index f99b10b2b4dd270f44c6ac8ec972b5e6fa18d0f7..53131f546aca34fb23d380e33c9b1af344dede94 100644 (file)
@@ -192,6 +192,9 @@ struct _GtkTreeViewPrivate
   guint has_rules : 1;
   guint mark_rows_col_dirty : 1;
 
+  /* for DnD */
+  guint empty_view_drop : 1;
+
   guint ctrl_pressed : 1;
   guint shift_pressed : 1;
   
index 2f7b5963c21c975940b2826a6eb064c4515c458e..65f3194bec5703cdc7606a024504a6095d918051 100644 (file)
@@ -1754,7 +1754,7 @@ gtk_tree_store_drag_data_received (GtkTreeDragDest   *drag_dest,
           gtk_tree_path_free (parent);
           parent = NULL;
 
-          gtk_tree_store_prepend (GTK_TREE_STORE (tree_model),
+          gtk_tree_store_prepend (tree_store,
                                   &dest_iter,
                                   dest_parent_p);
 
@@ -1762,36 +1762,17 @@ gtk_tree_store_drag_data_received (GtkTreeDragDest   *drag_dest,
         }
       else
         {
-          if (gtk_tree_model_get_iter (GTK_TREE_MODEL (tree_model),
-                                       &dest_iter,
-                                       prev))
+          if (gtk_tree_model_get_iter (tree_model, &dest_iter, prev))
             {
               GtkTreeIter tmp_iter = dest_iter;
 
-             if (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_model), "gtk-tree-model-drop-append")))
-               {
-                 GtkTreeIter parent;
-
-                 if (gtk_tree_model_iter_parent (GTK_TREE_MODEL (tree_model), &parent, &tmp_iter))
-                   gtk_tree_store_append (GTK_TREE_STORE (tree_model),
-                                          &dest_iter, &parent);
-                 else
-                   gtk_tree_store_append (GTK_TREE_STORE (tree_model),
-                                          &dest_iter, NULL);
-               }
-             else
-               gtk_tree_store_insert_after (GTK_TREE_STORE (tree_model),
-                                            &dest_iter,
-                                            NULL,
-                                            &tmp_iter);
-              retval = TRUE;
+              gtk_tree_store_insert_after (tree_store, &dest_iter, NULL,
+                                           &tmp_iter);
 
+              retval = TRUE;
             }
         }
 
-      g_object_set_data (G_OBJECT (tree_model), "gtk-tree-model-drop-append",
-                        NULL);
-
       gtk_tree_path_free (prev);
 
       /* If we succeeded in creating dest_iter, walk src_iter tree branch,
index 18f9ac2a020a8d9c2fbb7417b17fbab7402f333f..fd30de72f864397571dac503d44072f971aed9c4 100644 (file)
@@ -40,6 +40,7 @@
 #include "gtktreemodelsort.h"
 
 #define GTK_TREE_VIEW_SEARCH_DIALOG_KEY "gtk-tree-view-search-dialog"
+
 #define GTK_TREE_VIEW_PRIORITY_VALIDATE (GDK_PRIORITY_REDRAW + 5)
 #define GTK_TREE_VIEW_PRIORITY_SCROLL_SYNC (GTK_TREE_VIEW_PRIORITY_VALIDATE + 2)
 #define GTK_TREE_VIEW_NUM_ROWS_PER_IDLE 500
@@ -4808,26 +4809,79 @@ get_source_row (GdkDragContext *context)
     return NULL;
 }
 
+typedef struct
+{
+  GtkTreeRowReference *dest_row;
+  gboolean             path_down_mode;
+  gboolean             empty_view_drop;
+  gboolean             drop_append_mode;
+}
+DestRow;
+
+static void
+dest_row_free (gpointer data)
+{
+  DestRow *dr = (DestRow *)data;
+
+  gtk_tree_row_reference_free (dr->dest_row);
+  g_free (dr);
+}
+
 
 static void
 set_dest_row (GdkDragContext *context,
               GtkTreeModel   *model,
-              GtkTreePath    *dest_row)
+              GtkTreePath    *dest_row,
+              gboolean        path_down_mode,
+              gboolean        empty_view_drop,
+              gboolean        drop_append_mode)
 {
-  g_object_set_data_full (G_OBJECT (context),
-                          "gtk-tree-view-dest-row",
-                          dest_row ? gtk_tree_row_reference_new (model, dest_row) : NULL,
-                          (GDestroyNotify) (dest_row ? gtk_tree_row_reference_free : NULL));
+  DestRow *dr;
+
+  if (!dest_row)
+    {
+      g_object_set_data_full (G_OBJECT (context), "gtk-tree-view-dest-row",
+                              NULL, NULL);
+      return;
+    }
+
+  dr = g_new0 (DestRow, 1);
+
+  dr->dest_row = gtk_tree_row_reference_new (model, dest_row);
+  dr->path_down_mode = path_down_mode;
+  dr->empty_view_drop = empty_view_drop;
+  dr->drop_append_mode = drop_append_mode;
+
+  g_object_set_data_full (G_OBJECT (context), "gtk-tree-view-dest-row",
+                          dr, (GDestroyNotify) dest_row_free);
 }
 
 static GtkTreePath*
-get_dest_row (GdkDragContext *context)
+get_dest_row (GdkDragContext *context,
+              gboolean       *path_down_mode)
 {
-  GtkTreeRowReference *ref =
+  DestRow *dr =
     g_object_get_data (G_OBJECT (context), "gtk-tree-view-dest-row");
 
-  if (ref)
-    return gtk_tree_row_reference_get_path (ref);
+  if (dr)
+    {
+      GtkTreePath *path = NULL;
+
+      if (path_down_mode)
+        *path_down_mode = dr->path_down_mode;
+
+      if (dr->dest_row)
+        path = gtk_tree_row_reference_get_path (dr->dest_row);
+      else if (dr->empty_view_drop)
+        path = gtk_tree_path_new_from_indices (0, -1);
+      else
+        path = NULL;
+
+      if (path && dr->drop_append_mode)
+        gtk_tree_path_next (path);
+
+      return path;
+    }
   else
     return NULL;
 }
@@ -5071,6 +5125,7 @@ set_destination_row (GtkTreeView    *tree_view,
   TreeViewDragInfo *di;
   GtkWidget *widget;
   GtkTreePath *old_dest_path = NULL;
+  gboolean can_drop = FALSE;
 
   *suggested_action = 0;
   *target = GDK_NONE;
@@ -5106,18 +5161,34 @@ set_destination_row (GtkTreeView    *tree_view,
                                           &path,
                                           &pos))
     {
-      /* can't drop here */
+      gint n_children;
+      GtkTreeModel *model;
+
       remove_open_timeout (tree_view);
 
-      gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget),
-                                       NULL,
-                                       GTK_TREE_VIEW_DROP_BEFORE);
+      /* the row got dropped on empty space, let's setup a special case
+       */
 
       if (path)
        gtk_tree_path_free (path);
 
-      /* don't propagate to parent though */
-      return TRUE;
+      model = gtk_tree_view_get_model (tree_view);
+
+      n_children = gtk_tree_model_iter_n_children (model, NULL);
+      if (n_children)
+        {
+          pos = GTK_TREE_VIEW_DROP_AFTER;
+          path = gtk_tree_path_new_from_indices (n_children - 1, -1);
+        }
+      else
+        {
+          pos = GTK_TREE_VIEW_DROP_BEFORE;
+          path = gtk_tree_path_new_from_indices (0, -1);
+        }
+
+      can_drop = TRUE;
+
+      goto out;
     }
 
   g_assert (path);
@@ -5139,17 +5210,22 @@ set_destination_row (GtkTreeView    *tree_view,
     gtk_tree_path_free (old_dest_path);
 
   if (TRUE /* FIXME if the location droppable predicate */)
+    {
+      can_drop = TRUE;
+    }
+
+out:
+  if (can_drop)
     {
       GtkWidget *source_widget;
 
       *suggested_action = context->suggested_action;
-
       source_widget = gtk_drag_get_source_widget (context);
 
       if (source_widget == widget)
         {
           /* Default to MOVE, unless the user has
-           * pressed ctrl or alt to affect available actions
+           * pressed ctrl or shift to affect available actions
            */
           if ((context->actions & GDK_ACTION_MOVE) != 0)
             *suggested_action = GDK_ACTION_MOVE;
@@ -5173,14 +5249,22 @@ set_destination_row (GtkTreeView    *tree_view,
 
   return TRUE;
 }
-static GtkTreePath*
-get_logical_dest_row (GtkTreeView *tree_view)
 
+static GtkTreePath*
+get_logical_dest_row (GtkTreeView *tree_view,
+                      gboolean    *path_down_mode,
+                      gboolean    *drop_append_mode)
 {
   /* adjust path to point to the row the drop goes in front of */
   GtkTreePath *path = NULL;
   GtkTreeViewDropPosition pos;
 
+  g_return_val_if_fail (path_down_mode != NULL, NULL);
+  g_return_val_if_fail (drop_append_mode != NULL, NULL);
+
+  *path_down_mode = FALSE;
+  *drop_append_mode = 0;
+
   gtk_tree_view_get_drag_dest_row (tree_view, &path, &pos);
 
   if (path == NULL)
@@ -5190,10 +5274,7 @@ get_logical_dest_row (GtkTreeView *tree_view)
     ; /* do nothing */
   else if (pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE ||
            pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER)
-    {
-      /* get first child, drop before it */
-      gtk_tree_path_down (path);
-    }
+    *path_down_mode = TRUE;
   else
     {
       GtkTreeIter iter;
@@ -5201,17 +5282,14 @@ get_logical_dest_row (GtkTreeView *tree_view)
 
       g_assert (pos == GTK_TREE_VIEW_DROP_AFTER);
 
-      gtk_tree_model_get_iter (model, &iter, path);
-
-      if (!gtk_tree_model_iter_next (model, &iter))
-       g_object_set_data (G_OBJECT (model), "gtk-tree-model-drop-append",
-                          GINT_TO_POINTER (1));
+      if (!gtk_tree_model_get_iter (model, &iter, path) ||
+          !gtk_tree_model_iter_next (model, &iter))
+        *drop_append_mode = 1;
       else
         {
-         g_object_set_data (G_OBJECT (model), "gtk-tree-model-drop-append",
-                            NULL);
-         gtk_tree_path_next (path);
-       }
+          *drop_append_mode = 0;
+          gtk_tree_path_next (path);
+        }
     }
 
   return path;
@@ -5451,7 +5529,9 @@ gtk_tree_view_drag_motion (GtkWidget        *widget,
                            gint              y,
                            guint             time)
 {
+  gboolean empty;
   GtkTreePath *path = NULL;
+  GtkTreeModel *model;
   GtkTreeViewDropPosition pos;
   GtkTreeView *tree_view;
   GdkDragAction suggested_action = 0;
@@ -5464,7 +5544,11 @@ gtk_tree_view_drag_motion (GtkWidget        *widget,
 
   gtk_tree_view_get_drag_dest_row (tree_view, &path, &pos);
 
-  if (path == NULL)
+  /* we only know this *after* set_desination_row */
+  model = gtk_tree_view_get_model (tree_view);
+  empty = tree_view->priv->empty_view_drop;
+
+  if (path == NULL && !empty)
     {
       /* Can't drop here. */
       gdk_drag_status (context, 0, time);
@@ -5519,6 +5603,8 @@ gtk_tree_view_drag_drop (GtkWidget        *widget,
   GdkAtom target = GDK_NONE;
   TreeViewDragInfo *di;
   GtkTreeModel *model;
+  gboolean path_down_mode;
+  gboolean drop_append_mode;
 
   tree_view = GTK_TREE_VIEW (widget);
 
@@ -5538,7 +5624,7 @@ gtk_tree_view_drag_drop (GtkWidget        *widget,
   if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target))
     return FALSE;
 
-  path = get_logical_dest_row (tree_view);
+  path = get_logical_dest_row (tree_view, &path_down_mode, &drop_append_mode);
 
   if (target != GDK_NONE && path != NULL)
     {
@@ -5546,8 +5632,9 @@ gtk_tree_view_drag_drop (GtkWidget        *widget,
        * treat drag data receives as a drop.
        */
       set_status_pending (context, 0);
-
-      set_dest_row (context, model, path);
+      set_dest_row (context, model, path,
+                    path_down_mode, tree_view->priv->empty_view_drop,
+                    drop_append_mode);
     }
 
   if (path)
@@ -5583,6 +5670,8 @@ gtk_tree_view_drag_data_received (GtkWidget        *widget,
   GtkTreeView *tree_view;
   GtkTreePath *dest_row;
   GdkDragAction suggested_action;
+  gboolean path_down_mode;
+  gboolean drop_append_mode;
 
   tree_view = GTK_TREE_VIEW (widget);
 
@@ -5605,17 +5694,33 @@ gtk_tree_view_drag_data_received (GtkWidget        *widget,
        * supposed to call drag_status, not actually paste in the
        * data.
        */
-      path = get_logical_dest_row (tree_view);
+      path = get_logical_dest_row (tree_view, &path_down_mode,
+                                   &drop_append_mode);
 
       if (path == NULL)
         suggested_action = 0;
+      else if (path_down_mode)
+        gtk_tree_path_down (path);
 
       if (suggested_action)
         {
          if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
                                                     path,
                                                     selection_data))
-           suggested_action = 0;
+            {
+              if (path_down_mode)
+                {
+                  path_down_mode = FALSE;
+                  gtk_tree_path_up (path);
+
+                  if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
+                                                             path,
+                                                             selection_data))
+                    suggested_action = 0;
+                }
+              else
+               suggested_action = 0;
+            }
         }
 
       gdk_drag_status (context, suggested_action, time);
@@ -5632,11 +5737,22 @@ gtk_tree_view_drag_data_received (GtkWidget        *widget,
       return;
     }
 
-  dest_row = get_dest_row (context);
+  dest_row = get_dest_row (context, &path_down_mode);
 
   if (dest_row == NULL)
     return;
 
+  if (selection_data->length >= 0)
+    {
+      if (path_down_mode)
+        {
+          gtk_tree_path_down (dest_row);
+          if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
+                                                     dest_row, selection_data))
+            gtk_tree_path_up (dest_row);
+        }
+    }
+
   if (selection_data->length >= 0)
     {
       if (gtk_tree_drag_dest_drag_data_received (GTK_TREE_DRAG_DEST (model),
@@ -5661,7 +5777,7 @@ gtk_tree_view_drag_data_received (GtkWidget        *widget,
   gtk_tree_path_free (dest_row);
 
   /* drop dest_row */
-  set_dest_row (context, NULL, NULL);
+  set_dest_row (context, NULL, NULL, FALSE, FALSE, FALSE);
 }
 
 
@@ -10635,6 +10751,7 @@ gtk_tree_view_set_drag_dest_row (GtkTreeView            *tree_view,
                                  GtkTreeViewDropPosition pos)
 {
   GtkTreePath *current_dest;
+
   /* Note; this function is exported to allow a custom DND
    * implementation, so it can't touch TreeViewDragInfo
    */
@@ -10644,10 +10761,26 @@ gtk_tree_view_set_drag_dest_row (GtkTreeView            *tree_view,
   current_dest = NULL;
 
   if (tree_view->priv->drag_dest_row)
-    current_dest = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
+    {
+      current_dest = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
+      gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
+    }
 
-  if (tree_view->priv->drag_dest_row)
-    gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
+  /* special case a drop on an empty model */
+  tree_view->priv->empty_view_drop = 0;
+
+  if (pos == GTK_TREE_VIEW_DROP_BEFORE && path
+      && gtk_tree_path_get_depth (path) == 1
+      && gtk_tree_path_get_indices (path)[0] == 0)
+    {
+      gint n_children;
+
+      n_children = gtk_tree_model_iter_n_children (tree_view->priv->model,
+                                                   NULL);
+
+      if (!n_children)
+        tree_view->priv->empty_view_drop = 1;
+    }
 
   tree_view->priv->drag_dest_pos = pos;
 
@@ -10694,7 +10827,12 @@ gtk_tree_view_get_drag_dest_row (GtkTreeView              *tree_view,
       if (tree_view->priv->drag_dest_row)
         *path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
       else
-        *path = NULL;
+        {
+          if (tree_view->priv->empty_view_drop)
+            *path = gtk_tree_path_new_from_indices (0, -1);
+          else
+            *path = NULL;
+        }
     }
 
   if (pos)